#!/usr/bin/env python3
"""
EEG信号处理应用自动打包脚本
基于PyInstaller，自动处理打包过程并进行后处理优化
"""
import os
import sys
import shutil
import subprocess
import platform
import time
from pathlib import Path

# 打包配置
APP_NAME = "app"  # 应用程序名称
VERSION = "1.0.0"  # 版本号
ICON_FILE = "assets/images/app_icon.ico"  # 应用图标文件(如果有)
AUTHOR = "Your Name"  # 开发者名称
COMPANY_NAME = "Your Company"  # 公司名称
COPYRIGHT = f"Copyright © {time.strftime('%Y')} {AUTHOR}"  # 版权信息

# 打包选项
CONSOLE = True  # 是否显示控制台窗口
ONE_FILE = False  # 是否打包为单个文件
UPX = True  # 是否使用UPX压缩
DEBUG = False  # 是否包含调试信息
CLEAN = True  # 是否清理临时文件

# 资源目录 - 仅用于显示信息，PyInstaller会自动处理资源复制
RESOURCE_DIRS = [
    "assets",
    "config",
]


def print_header(message):
    """打印带格式的标题信息"""
    separator = "=" * 80
    print(f"\n{separator}")
    print(f"=== {message} ".ljust(len(separator) - 1, "="))
    print(f"{separator}\n")


def clean_build_dirs():
    """清理build和dist目录"""
    print_header("清理编译目录")

    dirs_to_clean = ["build", "dist", "__pycache__"]
    
    print(f"正在检查并清理 {len(dirs_to_clean)} 个主要目录...")

    for i, dir_name in enumerate(dirs_to_clean, 1):
        print(f"[{i}/{len(dirs_to_clean)}] 检查目录: {dir_name}")
        if os.path.exists(dir_name):
            print(f"  - 发现目录，正在删除 {dir_name}...")
            try:
                shutil.rmtree(dir_name)
                print(f"  ✓ 已成功删除 {dir_name}")
            except Exception as e:
                print(f"  × 删除 {dir_name} 失败: {str(e)}")
        else:
            print(f"  - {dir_name} 目录不存在，无需清理")

    # 清理所有__pycache__目录
    print("\n开始扫描项目中的所有 __pycache__ 目录...")
    
    pycache_dirs = []
    for root, dirs, files in os.walk("."):
        for d in dirs:
            if d == "__pycache__":
                pycache_dirs.append(os.path.join(root, d))
    
    if pycache_dirs:
        print(f"发现 {len(pycache_dirs)} 个 __pycache__ 目录需要清理")
        for i, pycache_path in enumerate(pycache_dirs, 1):
            try:
                print(f"[{i}/{len(pycache_dirs)}] 正在删除: {pycache_path}")
                shutil.rmtree(pycache_path)
                print(f"  ✓ 已成功删除")
            except Exception as e:
                print(f"  × 删除失败: {str(e)}")
    else:
        print("未找到任何 __pycache__ 目录")
        
    print("\n清理完成!")


def run_pyinstaller():
    """运行PyInstaller构建应用"""
    print_header("开始构建应用")

    # 检查PyInstaller是否已安装
    try:
        subprocess.run(
            ["pyinstaller", "--version"],
            check=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
    except (subprocess.SubprocessError, FileNotFoundError):
        print("× PyInstaller未安装，请先安装PyInstaller")
        print("  pip install pyinstaller")
        return False

    # 构建PyInstaller命令
    cmd = ["pyinstaller", "app.spec"]

    # 运行PyInstaller
    print(f"执行命令: {' '.join(cmd)}")
    print("正在构建应用，请耐心等待...")
    print("------------------------------------------------------------")
    start_time = time.time()

    try:
        # 使用Popen而不是run，这样可以实时获取输出
        process = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,  # 将stderr重定向到stdout
            text=True,
            universal_newlines=True,
            bufsize=1,
        )
        
        # 读取并显示实时输出
        output_lines = []
        for line in iter(process.stdout.readline, ''):
            line = line.rstrip()
            if line:
                print(f">> {line}")
                output_lines.append(line)
                
                # 添加进度指示
                if "Building" in line and "EXE" in line:
                    print(">> 正在构建可执行文件...")
                elif "Building" in line and "COLLECT" in line:
                    print(">> 正在收集文件...")
                elif "Building" in line and "PKG" in line:
                    print(">> 正在打包...")
                
        # 等待进程完成并获取返回码
        return_code = process.wait()
        
        print("------------------------------------------------------------")
        
        if return_code == 0:
            elapsed_time = time.time() - start_time
            print(f"✓ 构建成功! 耗时: {elapsed_time:.2f} 秒")
            return True
        else:
            print(f"× 构建失败，返回代码: {return_code}")
            return False
    except subprocess.SubprocessError as e:
        print(f"× 构建过程中发生错误: {str(e)}")
        return False


def post_process():
    """处理构建后的文件结构"""
    print_header("进行打包后处理")

    dist_dir = os.path.join("dist", APP_NAME)

    if not os.path.exists(dist_dir):
        print(f"× 错误: 目录 {dist_dir} 不存在，打包可能失败")
        return False

    print(f"正在处理打包后的文件结构...")
    print(f"目标目录: {dist_dir}")
    
    # 创建_internal目录 - 这是可选的，用于区分内部资源
    internal_dir = os.path.join(dist_dir, "_internal")
    os.makedirs(internal_dir, exist_ok=True)
    print(f"✓ 已创建 {internal_dir} 目录")
    
    # 创建版本信息文件
    print("正在创建版本信息文件...")
    version_info = {
        "app_name": APP_NAME,
        "version": VERSION,
        "author": AUTHOR,
        "company": COMPANY_NAME,
        "copyright": COPYRIGHT,
        "build_date": time.strftime("%Y-%m-%d %H:%M:%S"),
    }

    version_file_path = os.path.join(dist_dir, "version.txt")
    with open(version_file_path, "w", encoding="utf-8") as f:
        for key, value in version_info.items():
            f.write(f"{key}={value}\n")

    print(f"✓ 已创建版本信息文件: {version_file_path}")
    
    print("- 后处理完成")
    return True


def create_zip_archive():
    """创建分发ZIP归档文件"""
    print_header("创建ZIP归档")

    dist_dir = Path("dist")
    app_dir = dist_dir / APP_NAME

    if not app_dir.exists():
        print(f"× 错误: 目录 {app_dir} 不存在，无法创建ZIP文件")
        return False

    # 创建ZIP文件名
    zip_filename = f"{APP_NAME}-{VERSION}.zip"
    zip_path = dist_dir / zip_filename

    # 删除已存在的ZIP文件
    if zip_path.exists():
        print(f"- 正在删除旧版本的ZIP文件: {zip_filename}")
        zip_path.unlink()
        print(f"✓ 已删除旧版本的 {zip_filename}")

    # 创建ZIP文件
    print(f"正在创建ZIP归档文件: {zip_filename}")
    print(f"压缩 {app_dir} 目录中的内容...")
    
    try:
        print("- 压缩中，请稍候...")
        archive_path = shutil.make_archive(
            str(dist_dir / APP_NAME), "zip", root_dir=dist_dir, base_dir=APP_NAME
        )
        print(f"✓ 完成初始ZIP创建: {archive_path}")
        
        # 重命名ZIP文件
        os.rename(dist_dir / f"{APP_NAME}.zip", zip_path)
        print(f"✓ 已重命名为最终ZIP: {zip_path}")
        
        # 获取文件大小
        zip_size = os.path.getsize(zip_path)
        size_str = format_size(zip_size)
        print(f"✓ 已创建ZIP归档: {zip_path} ({size_str})")
        return True
    except Exception as e:
        print(f"× 创建ZIP归档失败: {str(e)}")
        return False


def format_size(size_bytes):
    """将字节大小转换为可读格式"""
    if size_bytes < 1024:
        return f"{size_bytes} 字节"
    elif size_bytes < 1024 * 1024:
        return f"{size_bytes / 1024:.2f} KB"
    elif size_bytes < 1024 * 1024 * 1024:
        return f"{size_bytes / (1024 * 1024):.2f} MB"
    else:
        return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"


def main():
    """主函数，执行整个打包流程"""
    print_header(f"开始打包 {APP_NAME} v{VERSION}")
    total_start_time = time.time()
    
    # 显示当前时间
    now = time.strftime("%Y-%m-%d %H:%M:%S")
    print(f"当前时间: {now}")
    print(f"操作系统: {platform.system()} {platform.release()}")
    print(f"Python版本: {platform.python_version()}")
    print("")
    
    # 显示资源目录信息
    print(f"通过app.spec配置的资源目录: {', '.join(RESOURCE_DIRS)}")
    print(f"PyInstaller将自动处理资源文件的复制")
    print("")
    
    # 显示打包进度
    current_step = 0
    total_steps = 4
    
    def update_progress(step_name):
        nonlocal current_step
        current_step += 1
        percent = (current_step / total_steps) * 100
        print(f"进度: [{current_step}/{total_steps}] {percent:.1f}% - {step_name}")
    
    # 步骤1: 清理旧的构建文件
    update_progress("清理旧的构建文件")
    if CLEAN:
        clean_build_dirs()
    else:
        print("跳过清理步骤 (CLEAN=False)")
    
    # 用点表示等待，让用户知道程序没有卡住
    print("")
    
    # 步骤2: 运行PyInstaller构建应用
    update_progress("构建应用")
    if not run_pyinstaller():
        print_header("打包失败 ×")
        return False
    
    print("")
    
    # 步骤3: 进行打包后处理
    update_progress("进行打包后处理")
    if not post_process():
        print_header("打包后处理失败 ×")
        return False
    
    print("")
    
    # 步骤4: 创建ZIP归档
    update_progress("创建分发包")
    create_zip_archive()
    
    # 完成
    total_elapsed_time = time.time() - total_start_time
    minutes, seconds = divmod(total_elapsed_time, 60)
    time_str = f"{int(minutes)}分{seconds:.1f}秒" if minutes > 0 else f"{seconds:.1f}秒"
    
    print_header(f"打包完成! 总耗时: {time_str} ✓")
    
    # 打印输出路径
    dist_dir = os.path.abspath(os.path.join("dist", APP_NAME))
    exe_path = os.path.join(dist_dir, APP_NAME + '.exe')
    zip_path = os.path.abspath(os.path.join('dist', f'{APP_NAME}-{VERSION}.zip'))
    internal_dir = os.path.join(dist_dir, "_internal")
    
    print("输出文件:")
    print(f"  应用程序目录: {dist_dir}")
    print(f"  内部资源目录: {internal_dir}")
    print(f"  可执行文件: {exe_path}")
    print(f"  ZIP归档: {zip_path}")
    
    # 检查文件是否存在
    if os.path.exists(exe_path):
        exe_size = format_size(os.path.getsize(exe_path))
        print(f"  可执行文件大小: {exe_size}")
    
    if os.path.exists(zip_path):
        zip_size = format_size(os.path.getsize(zip_path))
        print(f"  ZIP归档大小: {zip_size}")
    
    print("\n打包成功! 可以通过以下方式运行应用:")
    print(f"  1. 直接运行 {APP_NAME}.exe")
    print(f"  2. 解压 {APP_NAME}-{VERSION}.zip 后运行")
    print(f"\n注意: PyInstaller已通过app.spec自动处理了资源文件的复制")
    
    return True


if __name__ == "__main__":
    try:
        success = main()
        sys.exit(0 if success else 1)
    except KeyboardInterrupt:
        print("\n打包过程被用户中断")
        sys.exit(1)
    except Exception as e:
        print(f"\n发生错误: {str(e)}")
        import traceback

        traceback.print_exc()
        sys.exit(1)
